#pragma  once
#include "pdeVolume.h"
#include "pdeFile.h"
#include <fstream>
#include <sstream>
#include <iomanip>

void load_mhd_header(const std::string& fn, pde::Size& size, pde::Spacing& spacing, pde::Origin& origin, std::string& extra_info);

	template <class T>
	void save_volume_mhd(T* vol, pde::Size size, pde::Spacing spacing, pde::Origin origin, const std::string& fn, const std::string& extra_info)
	{
		//Matrix3x3 orientation = vol.get_orientation();
		double orien_ptr[] = {1,0,0,0,1,0,0,0,1};

		std::string type;
		if (typeid(T) == typeid(unsigned char)) type = "MET_UCHAR";
		else if (typeid(T) == typeid(char)) type = "MET_CHAR";
		else if (typeid(T) == typeid(unsigned short)) type = "MET_USHORT";
		else if (typeid(T) == typeid(short)) type = "MET_SHORT";
		else if (typeid(T) == typeid(float)) type = "MET_FLOAT";
		else if (typeid(T) == typeid(int)) type = "MET_INT";
		else {
			fprintf(stderr, "save_volume_mhd: unsupported type.\n");
			return;
		}

		// save mhd file
		std::fstream file;
		file.open(fn, std::ios::out);
		file << std::setiosflags(std::ios::fixed);
		file << std::setprecision(6);
		file << "ObjectType = Image" << std::endl;
		file << "NDims = 3" << std::endl;
		file << "BinaryData = True" << std::endl;
		file << "BinaryDataByteOrderMSB = False" << std::endl;
		file << "CompressedData = False" << std::endl;

		file << "TransformMatrix =";
		for (int i = 0; i < 9; i++)
			file << " " << orien_ptr[i];
		file << std::endl;

		file << "Offset =";
		for (int i = 0; i < 3; i++)
			file << " " << origin[i];
		file << std::endl;

		file << "CenterOfRotation = 0 0 0" << std::endl;

		file << "ElementSpacing =";
		for (int i = 0; i < 3; i++)
			file << " " << spacing[i];
		file << std::endl;

		file << "DimSize =";
		for (int i = 0; i < 3; i++)
			file << " " << size[i];
		file << std::endl;

		file << "ElementType = " << type << std::endl;

		std::string str = pde::baseName(fn) + ".raw";
		file << "ElementDataFile = " << str << std::endl;
		file << "ExtraInfo = " << extra_info << std::endl;
		file.close();

		// save raw file
		std::string raw_filepath = pde::fullBaseName(fn) + ".raw";
		file.open(raw_filepath, std::ios::out | std::ios::binary);
		file.write(reinterpret_cast<char*>(vol), size[0]*size[1]*size[2] * sizeof(T));
		file.close();

		return;
	}

	template <class T>
	void load_mhd_volume(T* vol, const std::string fn)
	{
		// Read file content
		std::ifstream inputStream;
		inputStream.open(fn.c_str(), std::ios::in);
		if (inputStream.fail())
		{
			fprintf(stderr, "open file failed: %s.\n", fn.c_str());
			return;
		}

		char* buf = new char[8001];
		inputStream.read(buf, 8000);
		unsigned long fileSize = (unsigned long)inputStream.gcount();
		buf[fileSize] = 0;
		std::string header(buf);
		header.resize(fileSize);
		delete[] buf;
		inputStream.close();

		// Check content
		std::string value;
		std::stringstream ss;
		pde::Size size;
		pde::Spacing spacing;
		pde::Origin origin;
		//Matrix3x3 orientation;
		//double* orien_ptr = glm::value_ptr(orientation);
		bool big_endian = false;

		//value = GetTagValue(header, "TransformMatrix");
		//if (value != "") {
		//	ss.clear();
		//	ss << value;
		//	for (int i = 0; i < 9; i++) {
		//		ss >> orien_ptr[i];
		//	}
		//}


		value = pde::GetTagValue(header, "Offset");
		if (value != "") {
			ss.clear();
			ss << value;
			for (int i = 0; i < 3; ++i) {
				ss >> origin[i];
			}
		}

		value = pde::GetTagValue(header, "ElementSpacing");
		if (value != "") {
			ss.clear();
			ss << value;
			for (int i = 0; i < 3; ++i) {
				ss >> spacing[i];
			}
		}

		value = pde::GetTagValue(header, "DimSize");
		if (value != "") {
			ss.clear();
			ss << value;
			for (int i = 0; i < 3; ++i) {
				ss >> size[i];
			}
		}

		value = pde::GetTagValue(header, "BinaryDataByteOrderMSB");
		if (value != "") {
			if (value == "True") {
				big_endian = true;
			}
		}


		value = pde::GetTagValue(header, "ElementType");
		if (value != "") {
			if (value == "MET_UCHAR")
			{
				if (typeid(T) != typeid(unsigned char)) {
					fprintf(stderr, "load_volume_mhd: mismatched pixel type.\n");
					return;
				}
			}
			else if (value == "MET_CHAR")
			{
				if (typeid(T) != typeid(char)) {
					fprintf(stderr, "load_volume_mhd: mismatched pixel type.\n");
					return;
				}
			}
			else if (value == "MET_USHORT")
			{
				if (typeid(T) != typeid(unsigned short)) {
					fprintf(stderr, "load_volume_mhd: mismatched pixel type.\n");
					return;
				}
			}
			else if (value == "MET_SHORT")
			{
				if (typeid(T) != typeid(short)) {
					fprintf(stderr, "load_volume_mhd: mismatched pixel type.\n");
					return;
				}
			}
			else if (value == "MET_FLOAT")
			{
				if (typeid(T) != typeid(float)) {
					fprintf(stderr, "load_volume_mhd: mismatched pixel type.\n");
					return;
				}
			}
			else if (value == "MET_INT")
			{
				if (typeid(T) != typeid(int)) {
					fprintf(stderr, "load_volume_mhd: mismatched pixel type.\n");
					return;
				}
			}
			else
			{
				fprintf(stderr, "load_volume_mhd: unsupported pixel type\n");
				return;
			}
		}


		// Read image data

		std::string pathName = pde::dirName(fn);
		std::string elementDataFileName = pde::GetTagValue(header, "ElementDataFile");
		std::string dataFileName;
		if (pathName == "")
			dataFileName = elementDataFileName;
		else
			dataFileName = pde::combine(pathName, elementDataFileName);

		inputStream.open(dataFileName.c_str(), std::ios::in | std::ios::binary);
		if (inputStream.fail())
		{
			printf("open file failed: %s\n", dataFileName.c_str());
			return;
		}
		inputStream.read((char*)vol, size[0]*size[1]*size[2] * sizeof(T));
		inputStream.close();

		if (big_endian && (typeid(T) == typeid(short) || typeid(T) == typeid(unsigned short)))
		{
			unsigned short* ptr = (unsigned short*)vol;
			for (int i = 0; i < size[0]*size[1]*size[2]; i++)
			{
				ptr[i] = _byteswap_ushort(ptr[i]);
			}
		}

		return;
	}
